#include "Renderer.h"
#include "resource.h"

Renderer* Renderer::m_pInstance = 0;

Renderer* Renderer::GetInstance(void)
{
	if(m_pInstance == 0)
	{
		m_pInstance = new Renderer();
	}

	return m_pInstance;
}

LRESULT CALLBACK Renderer::WndProc(HWND hw, UINT msg, WPARAM wp, LPARAM lp)
{
	Event e;

	POINTS mouse;

	switch (msg)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
		break;
	case WM_QUIT:
		DestroyWindow(hw);
		return 0;
		break;
	case WM_MOUSEMOVE:
		mouse = MAKEPOINTS(lp);
		e.Type = Event::EventType::Mouse;
		e.MouseX = mouse.x;
		e.MouseY = mouse.y;
		Renderer::GetInstance()->m_Events.push(e);
		return 0;
		break;
	default:
		return DefWindowProc(hw, msg, wp, lp);
		break;
	}
}

Renderer::Renderer(void)
{
	m_hDC = 0;
	m_hRC = 0;
	m_bInited = false;
}

Renderer::~Renderer(void)
{
	wglMakeCurrent(m_hDCOld, m_hRCOld);
	wglDeleteContext(m_hRC);
	ReleaseDC(m_hWnd, m_hDC);
	DestroyWindow(m_hWnd);
}

void Renderer::SetWindow(char* title, int width, int height, int bpp, bool bFullScreen)
{
	// If we have a window open, destroy it
	if(m_hWnd)
	{
		DestroyWindow(m_hWnd);
	}

	m_title = title;
	m_width = width;
	m_height = height;
	m_bpp = bpp;
	m_bFullScreen = bFullScreen;

	HANDLE m_hThread = CreateThread(0, 0, StartThread, this, 0, 0);

	while(!m_bInited) { }

	// Setup OpenGL
	m_hDC = GetDC(m_hWnd);

	PIXELFORMATDESCRIPTOR pfd;
	ZeroMemory(&pfd, sizeof(pfd));

	pfd.nSize = sizeof(pfd);
	pfd.nVersion = 1;
	pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
	pfd.iPixelType = PFD_TYPE_RGBA;
	pfd.cColorBits = m_bpp;
	pfd.cDepthBits = 16;
	pfd.iLayerType = PFD_MAIN_PLANE;
	int iFormat = ChoosePixelFormat(m_hDC, &pfd);
	SetPixelFormat(m_hDC, iFormat, &pfd);

	m_hRC = wglCreateContext(m_hDC);

	m_hDCOld = wglGetCurrentDC();
	m_hRCOld = wglGetCurrentContext();
	wglMakeCurrent(m_hDC, m_hRC);

	glClearColor(0.f, 0.f, 0.f, 1.f);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, 1, 1, 0, 0, 1);
	glMatrixMode(GL_MODELVIEW);

	glDisable(GL_DEPTH_TEST);
}

void Renderer::ToggleFullScreen(void)
{
}

bool Renderer::IsOpen(void)
{
	return (bool)IsWindowVisible(m_hWnd);
}

void Renderer::DrawScene(void)
{
	glClear(GL_COLOR_BUFFER_BIT);

	for(std::list<Sprite*>::iterator it = m_pSprites.begin(); it != m_pSprites.end(); it++)
	{
		(*it)->Draw();
	}

	SwapBuffers(m_hDC);
}

void Renderer::AddSprite(Sprite* sprite)
{
	m_pSprites.push_back(sprite);
}

void Renderer::RemSprite(Sprite* sprite)
{
	m_pSprites.remove(sprite);
}

DWORD WINAPI Renderer::StartThread(LPVOID pParam)
{
	Renderer* pWnd = (Renderer*)pParam;
	pWnd->Initialize();
	pWnd->MsgLoop();
	return 0;
}

void Renderer::Initialize(void)
{
	// Get the instance handle
	HINSTANCE hInst = GetModuleHandle(0);

	// Create a new window class
	WNDCLASS wc;
	ZeroMemory(&wc, sizeof(wc));
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_SHAPEGAME));
	wc.hInstance = hInst;
	wc.lpfnWndProc = Renderer::WndProc;
	wc.lpszClassName = WINDOWCLASS;
	wc.style = CS_OWNDC;

	// Register the new window class
	RegisterClass(&wc);

	// Adjust settings for full screen mode
	DWORD dwCreateFlags;
	if(m_bFullScreen)
	{
		dwCreateFlags = WS_POPUP;
	}
	else
	{
		dwCreateFlags = WS_OVERLAPPEDWINDOW;
	}

	// Create a new window
	m_hWnd = CreateWindow(
		WINDOWCLASS, m_title,
		dwCreateFlags,
		CW_USEDEFAULT, CW_USEDEFAULT, m_width, m_height,
		NULL, NULL, hInst, NULL);

	// Show and update the new window
	ShowWindow(m_hWnd, SW_NORMAL);
	UpdateWindow(m_hWnd);

	// Adjust Client Rectangle
	RECT wRect;
	GetWindowRect(m_hWnd, &wRect);

	RECT cRect;
	GetClientRect(m_hWnd, &cRect);

	int wAdjust = (wRect.right - wRect.left) - cRect.right;
	int hAdjust = (wRect.bottom - wRect.top) - cRect.bottom;

	MoveWindow(m_hWnd,
		wRect.left, wRect.top,
		m_width + wAdjust, m_height + hAdjust, true);

	m_bInited = true;
}

void Renderer::MsgLoop(void)
{
	MSG msg;
	ZeroMemory(&msg, sizeof(msg));

	while(this->IsOpen())
	{
		if(GetMessage(&msg, NULL, 0, 0))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		Sleep(1);
	}
}

bool Renderer::GetEvent(Event* e)
{
	if(m_Events.size() == 0)
	{
		return false;
	}

	Event ev = m_Events.front();
	m_Events.pop();

	memcpy(e, &ev, sizeof(ev));

	return true;
}